~S44PLAY.Xのココがポイント~ 3.矩形波に近い音とプログラムの同期 鎌田 誠
 S44PLAY.X では、FM 音源で矩形波に近い音を発生させてその振幅を PCM デー タに合わせて高速に変動させることで、PCM 音源をエミュレートしています。本 当は矩形波ではなくて一定の変位を保ったままの音(振動していないのだから音 とは言えませんが)を使いたいところなのですが、FM 音源がそのような出力を 生成することができないので、代わりに変位が安定している矩形波に近い音を使 っています。  矩形波に近い音を 4 つのチャンネルで 1/4 周期ずつずらしてキーオンするこ とで、常に 4 つのチャンネルうちの少なくともどれか 1 つは変位が安定した状 態を保っているようになっています。変位が安定しているチャンネルの振幅を PCM の変位に合わせて高速に振動させ、残りのチャンネルの振幅を 0 に保つこ とで、FM 音源 4 チャンネルで 1 チャンネルの PCM 音源をエミュレートするこ とができるというわけです。FM 音源は 8 チャンネル使えるので、合計 2 チャ ンネルの PCM 音源、つまりステレオの PCM 音源をエミュレートすることができ ます。  一定の変位を保ったままの波形ではなくて矩形波に近い波形を使うために生じ る問題があります。それは、「矩形波に近い波形は一定の時間が経つと変位が急 激に変化してしまう」というものです。矩形波なのですから変位が変化するのは 当たり前のことなのですが、問題は「矩形波に近い波の変位がいつ変化するかを 予測して、使用するチャンネルを適切なタイミングで次々と乗り換えてゆく処理 が非常に難しい」という点です。  X68k の FM 音源 LSI である YM2151 が出すことのできる最も波長が長い矩形 波に近い音の波長は、だいたい 0.103563 秒であることが実験的にわかっていま す(周波数に直すと約 9.656Hz ですから、もはや音とは言えないかも知れませ ん)。FM 音源から出力される音の波長はクロックに基づいているので極めて安 定しており、波長が勝手に変化することはないと考えられます。問題はどうやっ て、FM 音源が出す波の位相と、チャンネル遷移を決定するプログラムを、長時 間(最低でも数分~数十分)同期させるかです。  FM 音源に「出力している音の位相が 0 になる度に割り込み要求を出す」とい うような気の利いた機能が備わっていれば悩まなくて済むところなのですが、残 念ながら X68k では FM 音源から出力されている音の位相を MPU が直接監視す ることができません。ですから、他の方法で FM 音源の出力の位相を予測しなけ ればならないのです。  矩形波に近い音の 1 周期分の時間を計って MPU に知らせればよいわけですか ら、MFP のタイマー機能を使うのが手っとり早いと思われます。しかし、PCM デ ータを FM 音源に流し込む作業のために既に MFP のタイマー機能で 16~80μ秒 という短い間隔の割り込みが使用されています。その割り込みの回数を流用する としても、あるいは別の割り込みを併用するにしても、MPU が割り込みを取りこ ぼすことがあるのは必至です。MPU が割り込みを取りこぼす度に、FM 音源から 出ている矩形波に近い波と使用するチャンネルを決定するプログラムの同期がず れてゆき、そう長くない時間で正常に再生できなくなります(私はこれを“破綻 する”と表現しています)。1 周期の終わりを割り込みだけで判定することはで きないのです。  どうすれば破綻しないで済むか、考えました。最終的にチャンネルを切り替え る作業は MPU がやることですから、チャンネルを切り替えるべきタイミングに なったかどうかを MPU から逆にタイマーのほうに問い合わせることにすれば、 MPU がタイミングを見失う心配はなくなります。  MFP には Timer-A,Timer-B,Timer-C,Timer-D の 4 つがあります。Timer-A は 汎用タイマーまたは垂直同期カウンタとしてユーザに開放されています。 Timer-B はキーボードとの通信に使用しているので使用不可です(Timer-B を乗 っ取るとキーボードが使えなくなります)。Timer-C は IOCS が 1/100秒 カウ ンタとして使用しており、マウス処理、カーソル点滅、フロッピーディスクドラ イブのモーター OFF 制御、稼働時間の計測などに使われています。Timer-D は Human がバックグラウンドタスク用に使用しますが、CONFIG.SYS に PROCESS= の指定がない場合はユーザに開放されています。  S44PLAY.X では、Timer-D を乗っ取って(Human が使用しているときは Human から奪い取って)1μ秒 間隔でダウンカウントさせ、16~80μ秒 間隔(間隔は 再生開始時に決定する)で割り込みを発生させて、割り込みの度に PCM データ を 1 つずつ矩形波に近い音の振幅に反映させることで音を出しています。つま り Timer-D のカウンタは 79~0 の間を 1μ秒 間隔でダウンカウントしている わけですが、これでは期間が短すぎるので、矩形波の 1 周期の終わりを示すこ とはできません。  Timer-A はあえて残しておきたかったので、S44PLAY.X では矩形波の 1 周期 の終わりを決定するために Timer-C を使うことにしました。前述の通り、 Timer-C は IOCS が使用していますが、残しておいても MPU の負荷が増えて邪 魔なだけなので使ってしまうことにしました。Timer-C のカウンタを最も遅い 50μ秒 単位でダウンカウントするモードにして(これは IOCS の 1/100 秒カウ ンタと同じ設定です)、最も長い 256 回でオーバーフローするように設定しま す(この設定は初期の S44PLAY.X と異なっています)。 50μ秒×256=12800μ秒 ですから、Timer-C は 12800μ秒 間隔でオーバーフロ ーします。Timer-C に限らず、これが MFP のタイマー 1 つで直接測ることがで きる最も長い時間なのです。しかし、矩形波に近い音の波長をμ秒単位で表すと 103563μ秒 ですから、Timer-C のカウンタは 1 周期の終わりを直接示すことが できないことがわかります。具体的には、 103563μ秒÷12800μ秒=8余り1163μ秒、1163μ秒÷50μ秒=23.26 ですから、 矩形波に近い音の 1 周期の間に、Timer-C のカウンタは 8 回オーバーフローし てから 23.26 だけ減ることになります(実際には Timer-C のカウンタは整数な ので、23.26 というのは平均値です)。  MPU が間隔の短い割り込みを取りこぼす可能性があるので矩形波に近い音の 1 周期の終わりを調べるのに割り込みの回数を使うことができないと書きました。 しかし、多少の誤差があってもよければ、割り込み回数を使う方法でも「もうす ぐ 1 周期の終わりがやってくる」ということを予測することはできます。そこ で、Timer-D の割り込み回数を数えて Timer-C が 8 回オーバーフローする頃ま で進んだ後、Timer-C のカウンタを監視しながら進み、Timer-C のカウンタが前 回よりも 23.26 くらい減った値になったら 1 周期が終わったと判断することに しました。実際には Timer-C のカウンタは整数なので、平均して 23.26 減った 値を得るために「ワードサイズのワークから毎回 5955 ずつ引き、その上位バイ トを採用する」という方法を用いて、待つべき Timer-C のカウンタの値を予測 しています。この方法で決定される 1 周期は (256×8+5955÷256)×50μ秒=103563.0859375μ秒 となり、目標よりもわずか に長くなってしまいますが、誤差の範囲です。 ━─────────────────────────────────── ● S44PLAY.X のバージョンアップ状況(v1.01 → v1.02、修正・変更完了箇所)  Unsigned-8bit WAVE に対応しました。  入力データのサンプリング周波数を強制的に指定するオプション -freq を追 加しました。  標準入力を示す - が使えなくなっていた不具合を修正しました。  ファイルの種類の自動認識に失敗したとき、そのファイルだけを諦めて次のフ ァイルを再生するようにしました。  ステレオのデータをモノラル再生するときのバリエーションとして -left、 -right、-diff を追加しました。-left は左側の音だけ再生、-right は右側の 音だけ再生、-diff は左側の音から右側の音を引いて再生します。-diff は中央 の音を消してしまうので、素材によっては面白い効果が得られます。  FMP ファイルの再生専用の実行プログラム FMPPLAY.X を添付しました。ソー スは S44PLAY.X と共通で、アセンブルスイッチを変えることで両方生成できる ようになっています。  データ変換モードで、先頭のデータがモノラルならば、パラメータの数に関係 なく -mono にすることにしました。  データ変換モードで自動的に -mono に切り替えたときに FMP ファイルのヘッ ダがステレオを示したままになっていた不具合を修正しました。  Mach-2 または mach2p を使用しているとローカルメモリで動かない場合があ る不具合を修正しました。  その他細かい点を幾つか修正・変更しました。 (EOF)